#pragma once

#include <memory>
#include <string>
#include <cstring>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include "Log.hpp"
#include "Common.hpp"
#include "InetAddr.hpp"

const static int g_socket = -1;
const static int g_port = 8080;

using namespace MyLogModule;
class UdpServer
{
public:
    UdpServer(uint16_t port = g_port)
        : _socket(g_socket)
        , _addr(port)   //服务端创建自己的port与ip,直接使用port初始化InetAddr对象，内部自动填充好了
        , _isRunning(false)
    {
    }

    ~UdpServer()
    {
        if(_socket > 0)
            close(_socket);
    }

    void Init()
    {
        // 1. 创建网络socket
        _socket = ::socket(AF_INET, SOCK_DGRAM, 0);
        if (_socket == -1)
        {
            LOG(FATAL) << "socket create fail";
            Die(SOCKET_ERR);
        }

        LOG(NORMAL) << "file fd:" << _socket;
        // 2. 向套接字中填充网络信息--无需再做

        // 3. 设置套接字进入内核
        int n = ::bind(_socket, _addr.Getaddr(), _addr.Getlen());
        if (n == -1)
        {
            LOG(FATAL) << "bind fail";
            Die(BIND_ERR);
        }
        LOG(NORMAL) << "bind success";
    }

    void Start()
    {
        if (!_isRunning)
        {
            _isRunning = true;
            while (true)
            {
                //输出型参数
                struct sockaddr_in fromUser;
                socklen_t len = sizeof(fromUser);
                char buffer[1024];
                int n = ::recvfrom(_socket, &buffer, sizeof(buffer)-1, 0, CONVERSE(&fromUser), &len);
                if (n > 0)
                {
                    buffer[n] = 0;

                    InetAddr clientAddr(fromUser);  //直接拷贝构建InetAddr对象，对象内就已经转换好了
                    std::string message = clientAddr.getIP()  + " |"+std::to_string(clientAddr.getPort())+ "#:";
                    message += buffer;
                    LOG(DEBUG) << message;

                    std::string echoString = "echo say#";
                    echoString += buffer;
                    // 将消息在回显给发送方
                    ::sendto(_socket, echoString.c_str(), echoString.size(), 0, CONVERSE(&fromUser), len);
                }
            }
        }
        _isRunning = false;
    }

private:
    int _socket;
    InetAddr _addr;
    bool _isRunning;
};
